package cadtoolbox.optimizer;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.concurrent.Callable;

import cadtoolbox.model.OligoSystem;
import cadtoolbox.model.SequenceVertex;
import cadtoolbox.model.OligoGraph;
import cadtoolbox.optimizer.OptimizerCMAES.ReferencePoint;
import cadtoolbox.utils.MyWorker;
import cadtoolbox.utils.PluggableWorker;

public class FitnessEvaluation implements Callable, PluggableWorker{

	public double fitness = 0.0;
	public int position;
	protected double[] indiv;
	protected ArrayList<Parameter> parameters;
	protected ArrayList<ReferencePoint> targets;
	protected OligoGraph<SequenceVertex,String> graph;
	private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
	private boolean cancel = false;
	
	public FitnessEvaluation(int position, double[] indiv, ArrayList<ReferencePoint> targets, ArrayList<Parameter> params, OligoGraph<SequenceVertex,String> myGraph){
		//super(FitnessEvaluation.getOligoSystem(params, myGraph, indiv),myGraph);
		this.position = position;
		this.indiv = indiv;
		this.targets = targets;
		this.parameters = params;
		this.graph = (OligoGraph<SequenceVertex,String>) myGraph.clone();
	}
	
	public double valueOf(double[] x) {
		double dist = 0;
		double[][] timeSerie = generateTimeSerie(x);
		for(ReferencePoint ref: targets){
			dist += (timeSerie[ref.index][ref.time]-ref.target)*(timeSerie[ref.index][ref.time]-ref.target);
		}
		return dist;
	}

	private double[][] generateTimeSerie(double[] x) {
		OligoSystem<String> syst = FitnessEvaluation.getOligoSystem(parameters, graph, x);
		return syst.calculateTimeSeries(this);
	}

	protected static OligoSystem<String> getOligoSystem(ArrayList<Parameter> parameters, OligoGraph graph, double[] x){
		for(int i = 0; i < parameters.size(); i++){
			double transformedx = 0.5 - Math.cos(x[i]*Math.PI)/2.0;
			parameters.get(i).externalModification(transformedx*(parameters.get(i).maxValue-parameters.get(i).minValue)+parameters.get(i).minValue, graph);
		}
		OligoSystem<String> syst = new OligoSystem<String>(graph);
		return syst;
	}
	
	@Override
	public Object call() throws Exception {
		this.fitness = valueOf(this.indiv);
		return this;
	}

	@Override
	public void setCustomProgress(int time) {
		this.pcs.firePropertyChange("progress", time-1, time);
	}
	
	 public void addPropertyChangeListener(PropertyChangeListener listener) {
         this.pcs.addPropertyChangeListener(listener);
     }

     public void removePropertyChangeListener(PropertyChangeListener listener) {
         this.pcs.removePropertyChangeListener(listener);
     }

	@Override
	public void cancel() {
		this.cancel  = true;
	}
	
}
